/*
 * Copyright (c) 2007-2011, Petr Panteleyev
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright notice,
 *       this list of conditions and the following disclaimer in the documentation
 *       and/or other materials provided with the distribution.
 *    3. The name of the author may not be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.panteleyev.blogclient;

import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.EventObject;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.KeyStroke;

import org.panteleyev.blogapi.IBlogEntry;
import org.panteleyev.swing.WindowManager;
import org.panteleyev.swing.WindowManagerEnumerator;
import org.panteleyev.utilities.MD5;

/**
 *
 * @author Petr Panteleyev
 */
public abstract class UserProfile<E> implements Serializable {
    private final static long serialVersionUID = 20100206L;

    protected class HistoryFrameAction extends AbstractAction {
        public HistoryFrameAction() {
            super(L10N.HISTORY_WINDOW_TITLE.s(), Icon.HISTORY.i());
            putValue(Action.SHORT_DESCRIPTION, L10N.HISTORY_WINDOW_TITLE.s());
            putValue(Action.LARGE_ICON_KEY, Icon.HISTORY.I());
        }

        @Override
        public void actionPerformed(ActionEvent evt) {
            onHistoryFrameAction();
        }
    }

    protected class OutboxFrameAction extends AbstractAction {
        public OutboxFrameAction() {
            super("Outbox", Icon.OUTBOX.i());
            putValue(Action.SHORT_DESCRIPTION, "Outbox");
            putValue(Action.LARGE_ICON_KEY, Icon.OUTBOX.I());
        }

        @Override
        public void actionPerformed(ActionEvent evt) {
            onOutboxFrameAction();
        }
    }

    /* Serializable fields */
    private String          name = "";
    private UserProfileType type;
    private String          server = "";
    private String          userName = "";
    private String          passwd = "";
    private String          hPasswd = "";
    private boolean         storePasswd = true;
    private String          cacheId = "";
    private boolean         defaultProfile;

    /* Transient fields */
    private transient String            passwdTemp;
    private transient String            hPasswdTemp;
    private transient boolean           online;
    private transient AbstractAction    newPostAction;
    private transient boolean           connected;

    /** Creates a new instance of UserProfile
     * @param type
     */
    public UserProfile(UserProfileType type) {
        this.type = type;
        connected = false;

        createActions();
    }

    public UserProfile(UserProfileType type, String name, String server) {
        this.name = name;
        this.type = type;
        this.server = server;
        connected = false;

        createActions();
    }

    private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException {
        stream.defaultReadObject();
        createActions();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public boolean isOnline() {
        return online;
    }

    public void setOnline(boolean online) {
        this.online = online;
    }

    public String getServer() {
        return server;
    }

    public void setServer(String server) {
        this.server = server;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String name) {
        userName = name;
    }

    public String getHPasswd() {
        return hPasswd;
    }

    public String getCacheId() {
        return cacheId;
    }

    public void generateCacheId() {
        cacheId = MD5.encode(new Date().toString());
    }

    public void setPassword(String pwd) {
        passwd = pwd;
        if (passwd.length() != 0) {
            hPasswd = MD5.encode(passwd);
        } else {
            hPasswd = passwd;
        }
    }

    public String getPassword() {
        return passwd;
    }

    public boolean getStorePasswd() {
        return storePasswd;
    }

    public void setStorePasswd(boolean b) {
        storePasswd = b;
    }

    public UserProfileType getProfileType() {
        return type;
    }

    public void setProfileType(UserProfileType type) {
        this.type = type;
    }

    public boolean isConnected() {
        return connected;
    }

    public void setConnected(boolean connected) {
        this.connected = connected;
    }

    public String getHPasswdTemp() {
        return hPasswdTemp;
    }

    public String getPasswdTemp() {
        return passwdTemp;
    }

    public void setHPasswdTemp(String hPasswdTemp) {
        this.hPasswdTemp = hPasswdTemp;
    }

    public void setPasswdTemp(String passwdTemp) {
        this.passwdTemp = passwdTemp;
        if (passwdTemp.length() != 0) {
            hPasswdTemp = MD5.encode(passwdTemp);
        } else {
            hPasswdTemp = passwdTemp;
        }
    }

    public void applyPasswdTemp() {
        passwd = passwdTemp;
        hPasswd = hPasswdTemp;
    }

    public boolean isDefaultProfile() {
        return defaultProfile;
    }

    public void setDefaultProfile(boolean defaultProfile) {
        this.defaultProfile = defaultProfile;
        if (defaultProfile) {
            UserProfileManager.setDefaultProfile(this);
        }
    }

    public File getCacheDirectory() {
        File rootProfDir = new File(Options.getHomeDir(), "cache");
        if (!rootProfDir.exists()) {
            rootProfDir.mkdir();
        }

        File profDir = new File(rootProfDir, getCacheId());
        if (!profDir.exists()) {
            profDir.mkdir();
        }

        return profDir;
    }

    public File getOutboxDirectory() {
        File outbox = new File(getCacheDirectory(), "outbox");
        if (!outbox.exists()) {
            outbox.mkdir();
        }

        return outbox;
    }

    private void createActions() {
        newPostAction = new AbstractAction(L10N.MAIN_FRAME_NEW_POST_ACTION.s(), Icon.NEW_POST.i()) {
            @Override
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                onNewPost();
            }
        };
        newPostAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_N, BloggingKit.getKeyMask()));
    }

    public AbstractAction getNewPostAction() {
        return newPostAction;
    }

    public abstract ProfileDialog createProfileDialog(BloggerFrame<?> frame);
    public abstract ArrayList<AbstractAction> getProfileActions();
    public abstract ArrayList<AbstractAction> getProfileToolBarActions();
    public abstract String getProfileMenuName();
    public abstract ImageIcon getIcon();

    public abstract boolean login(boolean store, boolean offl);
    public abstract void postLoginUIActions();
    public abstract void onNewPost();

    public abstract IBlogEntry newBlogEntry();

    /* Helper methods */
    public abstract void openBlogEntryEditor(E entry, File file);
    public abstract BlogPostEditorFrame<?,?> findBlogEntryEditor(E entry);
    public abstract void publishEntry(E entry) throws Exception;
    public abstract void updateEntryAdterRestore(E entry);

    public void openStartupWindow() {
        onNewPost();
    }

    public abstract void onHistoryFrameAction();
    public abstract void onOutboxFrameAction();

    protected BloggerFrame <? extends UserProfile<?>> findFrame(Class<? extends BloggerFrame<?>> frameClass) {
        return WindowManager.find(frameClass, new WindowManagerEnumerator<BloggerFrame>() {
            @Override
            public boolean frameFound(BloggerFrame frame) {
                return (frame.getProfile() == UserProfile.this);
            }
        });
    }

    public void sendApplicationEvent(final EventObject event) {
        WindowManager.enumerate(BloggerFrame.class, new WindowManagerEnumerator<BloggerFrame>() {
            @Override
            public boolean frameFound(BloggerFrame frame) {
                if (frame.getProfile() == UserProfile.this && event.getSource() != frame) {
                    frame.receive(event);
                }
                return false;
            }
        });
    }

    public int getFramesCount(final Class<? extends BloggerFrame> frameClass) {
        class FrameCounter implements WindowManagerEnumerator<BloggerFrame> {
            private int counter;

            public int getCounter() {
                return counter;
            }

            @Override
            public boolean frameFound(BloggerFrame frame) {
                if (frame.getProfile() == UserProfile.this && frame.getClass().equals(frameClass)) {
                    ++counter;
                }
                return false;
            }

        }

        FrameCounter enumerator = new FrameCounter();

        WindowManager.enumerate(BloggerFrame.class, enumerator);
        return enumerator.getCounter();
    }

}
